﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TpLogic.Hamurabi
{
  
    public class GameState
    {
        public int allTimeDeadPeople { get; set; }
        public int percentStarved { get; set; }
        public int year { get; set; }
        public int population { get; set; }
        public int storedFood { get; set; }
        public int foodCreatedThisYear { get; set; }
        public int eaten  { get; set; }
        public int yield  { get; set; }
        public int acres { get; set; }
        public int immigrants { get; set; }
        //int Q = 1;//Stared at one to indicate no plague

        public bool gotImpeached { get; set; }
        public bool quitEarly { get; set; }
        public bool nextYearIsPlague { get; set; }

        public int deadPeople { get; set; }
        public bool gameOver { get; set; }
    }
        /// <summary>
        /// Depends on keyboard and random number generator
        /// </summary>
    public class HamurabiGame
    {
        public enum FinalReport
        {
            Best = 0,
            Good = 1,
            Bad = 2,
            Worse = 3
        }

        private readonly Random random;
        private readonly IConsole console;

        public HamurabiGame(Random generator, IConsole screen)
        {
            random = generator;
            console = screen;
        }

        public HamurabiGame()
        {
            random = new Random();
        }

        public GameState InitialGameState()
        {
            GameState  state = new GameState();
            state.allTimeDeadPeople = 0;
            state.percentStarved = 0;
            state.year = 0;
            state.population = 95;
            state.storedFood = 2800;
            state.foodCreatedThisYear = 3000;
            state.eaten = state.foodCreatedThisYear - state.storedFood;
            state.yield = 3;
            state.acres = state.foodCreatedThisYear / state.yield;
            state.immigrants = 5;
            //int Q = 1;//Stared at one to indicate no plague

            state.gotImpeached = false;
            state.quitEarly = false;
            state.nextYearIsPlague = false;

            state.deadPeople = 0;
            state.gameOver = false;
            return state;
        }


            public void PlayHammurabi(GameState state)
        {
            // CONVERTED FROM THE ORIGINAL FOCAL PROGRAM AND MODIFIED
            //_20: ; // FOR EDUSYSTEM 70 BY DAVID AHL, DIGITAL
            //_30: ; // MODIFIED FOR 8K MICROSOFT BASIC BY PETER TURNBULL
            //

            console.WriteLine("TRY YOUR HAND AT GOVERNING ANCIENT SUMERIA");
            console.WriteLine("SUCCESSFULLY FOR A 10-YR TERM OF OFFICE.");
            console.WriteLine();

            
            //TODO: To support ASP.NET
            //everytime user input is required, we must return GameState & wait for data entry & postback
            //then on PlayHammurabi, we extract a "resume point" and skip forward to that point in the 
            //main game loop.

            while (true)
            {
                state.year = state.year + 1;

                console.WriteLine("\n\nHAMURABI:  I BEG TO REPORT TO YOU,");
                console.WriteLine("IN YEAR {0} , {1} PEOPLE STARVED, {2} CAME TO THE CITY.", state.year, state.deadPeople, state.immigrants);

                state.population = state.population + state.immigrants;

                //Plauge is random, but not 1st year
                if (state.nextYearIsPlague)
                {
                    state.population = (state.population / 2);
                    console.WriteLine("A HORRIBLE PLAGUE STRUCK!  HALF THE PEOPLE DIED.");
                    state.nextYearIsPlague = false;
                }

                console.WriteLine(@"POPULATION IS NOW {0}
THE CITY NOW OWNS {1} ACRES.
YOU HARVESTED {2} BUSHELS PER ACRE.
RATS ATE {3} BUSHELS.
YOU NOW HAVE {4} BUSHELS IN STORE.\n\n",
                        state.population, state.acres, state.yield, state.eaten, state.storedFood);

                if (state.year == 11)
                {
                    break;
                }

                int yieldPerAcreDrift = random.Next(0, 11);
                state.yield = yieldPerAcreDrift + 17;

                console.WriteLine("LAND IS TRADING AT {0} BUSHELS PER ACRE.", state.yield);


                int acresToBuy = GetGrainInput("HOW MANY ACRES DO YOU WISH TO BUY? ", state.yield, state.storedFood);
                if (acresToBuy < 0)
                {
                    state.quitEarly = true;
                }

                //Wants to buy
                if (acresToBuy > 0)
                {
                    state.acres = state.acres + acresToBuy;
                    state.storedFood = state.storedFood - state.yield * acresToBuy;
                }
                else //Wanted to buy, so don't need to ask about sales.
                {
                    int acresToSell = GetAcresInput("HOW MANY ACRES DO YOU WISH TO SELL? ", state.acres);
                    if (acresToSell < 0)
                    {
                        state.quitEarly = true;
                    }
                    state.acres = state.acres - acresToSell;
                    state.storedFood = state.storedFood + state.yield * acresToSell;
                }
                console.WriteLine();


                int bushelsToFeed = GetBushelsToFeed(state.storedFood, state.yield);
                if (bushelsToFeed < 0)
                {
                    state.quitEarly = true;
                    break;
                }

                state.storedFood = state.storedFood - bushelsToFeed;

                console.WriteLine();

                int acresPlanted = GetAcresToPlantWithSeed(state.acres, state.storedFood, state.population, state.yield);

                if (acresPlanted < 0)
                {
                    state.quitEarly = true;
                    state.gameOver = true;
                    break;
                }

                if (acresPlanted != 0)
                {
                    state.storedFood = state.storedFood - (acresPlanted / 2);
                }

                // A BOUNTYFULL HARVEST!!
                state.yield = RollDice(); //Field harvest in units of 1 unit of food suitable for 1/20 of a person to eat 1 year.
                state.foodCreatedThisYear = acresPlanted * state.yield;
                state.eaten = 0;


                int ratCheck = RollDice();
                if (ratCheck / 2 == ratCheck / 2) //??? What
                {
                    // THE RATS ARE RUNNING WILD!!
                    state.eaten = (state.storedFood / ratCheck);
                }

                state.storedFood = state.storedFood - state.eaten + state.foodCreatedThisYear;
                state.immigrants = (RollDice() * (20 * state.acres + state.storedFood) / state.population / 100 + 1); // LET'S HAVE SOME BABIES
                int peopleWhoAteWell = (bushelsToFeed / 20); // HOW MANY PEOPLE HAD FULL TUMMIES?


                // HORRORS, A 15% CHANCE OF PLAGUE
                if ((int)(10 * (2 * random.NextDouble() - .3)) < 0)
                {
                    state.nextYearIsPlague = true;
                }

                if (state.population < peopleWhoAteWell)
                {
                    state.deadPeople = 0;
                    //continue;
                }
                else
                {
                    // state.yield; //If you didn't eat well, I guess you're dead.
                    state.deadPeople = state.population - peopleWhoAteWell;
                }
                

                // STARVE ENOUGH FOR IMPEACHMENT?
                if (state.deadPeople > .45 * state.population)
                {
                    state.gotImpeached = true;
                    console.WriteLine(@"YOU STARVED {0} PEOPLE IN ONE YEAR!!!
DUE TO THIS EXTREME MISMANAGEMENT YOU HAVE NOT ONLY
BEEN IMPEACHED AND THROWN OUT OF OFFICE BUT YOU HAVE
ALSO BEEN DECLARED 'NATIONAL FINK' !!", state.deadPeople);
                    state.gameOver = true;
                    break;
                }
                state.percentStarved = ((state.year - 1) * state.percentStarved + state.deadPeople * 100 / state.population) / state.year;

                state.population = peopleWhoAteWell;// state.yield; //Immigration accounted for at start of loop.

                state.allTimeDeadPeople = state.allTimeDeadPeople + state.deadPeople;
                //END OF MAIN GAME LOOP
            }


            if (!state.gotImpeached && !state.quitEarly)
            {
                console.WriteLine("IN YOUR 10-YEAR TERM OF OFFICE, " + state.percentStarved + " PERCENT OF THE");
                console.WriteLine("POPULATION STARVED PER YEAR ON AVERAGE, I.E., A TOTAL OF");
                console.WriteLine(state.allTimeDeadPeople + " PEOPLE DIED!!");
                float acresPerPerson = state.acres / state.population;
                console.WriteLine("YOU STARTED WITH 10 ACRES PER PERSON AND ENDED WITH");
                console.WriteLine(acresPerPerson + " ACRES PER PERSON.");
                console.WriteLine();


                FinalReport grade = FinalReport.Best;

                if (state.percentStarved > 33 || acresPerPerson < 7) grade = FinalReport.Worse; // goto _YourAFink;
                if (state.percentStarved > 10 || acresPerPerson < 9) grade = FinalReport.Bad; // goto _940;
                if (state.percentStarved > 3 || acresPerPerson < 10) grade = FinalReport.Good; //goto _960;

                switch (grade)
                {
                    case FinalReport.Worse:
                        console.WriteLine(
                            @"DUE TO THIS EXTREME MISMANAGEMENT YOU HAVE NOT ONLY
BEEN IMPEACHED AND THROWN OUT OF OFFICE BUT YOU HAVE
ALSO BEEN DECLARED 'NATIONAL FINK' !!");
                        break;
                    case FinalReport.Bad:
                        console.WriteLine(
                            @"YOUR HEAVY-HANDED PERFORMANCE SMACKS OF NERO AND IVAN IV.
THE PEOPLE (REMAINING) FIND YOU AN UNPLEASANT RULER, AND,
FRANKLY, HATE YOUR GUTS!");
                        break;
                    case FinalReport.Good:
                        console.WriteLine(
                            @"YOUR PERFORMANCE COULD HAVE BEEN SOMEWHAT BETTER, BUT
REALLY WASN'T TOO BAD AT ALL. {0} PEOPLE WOULD
DEARLY LIKE TO SEE YOU ASSASSINATED BUT WE ALL HAVE OUR
TRIVIAL PROBLEMS.", (int)(state.population * .8 * random.NextDouble()));
                        break;
                    default:
                        console.WriteLine("A FANTASTIC PERFORMANCE!!!  CHARLEMANGE, DISRAELI, AND");
                        console.WriteLine("JEFFERSON COMBINED COULD NOT HAVE DONE BETTER!");
                        break;
                }
                state.gameOver = true;
            }

            EndGame(state.quitEarly);
        }

        private int GetBushelsToFeed(int storedFood, int yield)
        {
            int bushelsToFeed;
            while (true)
            {
                console.Write("HOW MANY BUSHELS DO YOU WISH TO FEED YOUR PEOPLE? ");

                bushelsToFeed = Convert.ToInt32(console.ReadLine());


                // TRYING TO USE MORE GRAIN THAN IN THE SILOS?
                if (!(bushelsToFeed <= storedFood))
                {
                    NotEnoughGrain(yield, bushelsToFeed, storedFood);
                    continue;
                }
                break;
            }
            return bushelsToFeed;
        }

        private int GetAcresToPlantWithSeed(int acres, int storedFood, int population, int yield)
        {
            int acresPlanted;
            while (true)
            {

                console.Write("HOW MANY ACRES DO YOU WISH TO PLANT WITH SEED? ");
                string userInput = console.ReadLine();
                acresPlanted = Convert.ToInt32(userInput);

                // TRYING TO PLANT MORE ACRES THAN YOU OWN?
                if (!(acresPlanted <= acres))
                {
                    NotEnoughAcres(acresPlanted, acres);
                    continue;
                }

                // ENOUGH GRAIN FOR SEED?
                if (!(((acresPlanted / 2)) <= storedFood))
                {
                    NotEnoughGrain(yield, acresPlanted, storedFood);
                    continue;
                }

                // ENOUGH PEOPLE TO TEND THE CROPS?
                if (!(acresPlanted < 10 * population))
                {
                    console.WriteLine("BUT YOU HAVE ONLY " + population + " PEOPLE TO TEND THE FIELDS. NOW THEN,");
                    continue;
                }

                break; //Made it past the validations, get out of here.
            }
            return acresPlanted;
        }

        private void EndGame(bool quitEarly)
        {
            if (quitEarly)
            {
                console.WriteLine(
                    @"HAMURABI:  I CANNOT DO WHAT YOU WISH.
GET YOURSELF ANOTHER STEWARD!!!!!");

            }
            console.WriteLine();
            for (int n = 1; n <= 10; n++)
            {
                /* print char(7) ring bell*/
                ;
            }
            console.WriteLine("SO LONG FOR NOW.");
            console.WriteLine();
            console.ReadKey();
        }


        private int GetAcresInput(string question, int acres)
        {
            int quantity;
            do
            {
                console.Write(question);
                string keyboardInput = console.ReadLine();
                quantity = Convert.ToInt32(keyboardInput);
            } while (NotEnoughAcres(quantity, acres));
            return quantity;
        }

        private int GetGrainInput(string question, int yield, int storedFood)
        {
            int result;
            do
            {
                console.Write(question);
                string input = console.ReadLine();
                result = Convert.ToInt32(input);
            } while (NotEnoughGrain(yield, result, storedFood));

            return result;
        }

        private int RollDice()
        {
            return (int)(random.NextDouble() * 5) + 1;
        }

        private bool NotEnoughAcres(int q, int acres)
        {
            if (q > acres)
            {
                console.WriteLine("HAMURABI:  THINK AGAIN. YOU OWN ONLY " + acres + " ACRES.  NOW THEN,");
                return true;
            }
            return false;
        }

        private bool NotEnoughGrain(int yield, int q, int storedFood)
        {
            if (yield * q > storedFood)
            {
                console.WriteLine("HAMURABI:  THINK AGAIN. YOU HAVE ONLY {0} BUSHELS OF GRAIN.  NOW THEN,", storedFood);
                return true;
            }
            return false;
        }
    }
}
